package com.suray.attendance.controller;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.suray.attendance.common.dingding.service.DingAccessTokenService;
import com.suray.attendance.common.result.Result;
import com.suray.attendance.common.utils.LoggerUtil;
import com.suray.attendance.connect.ErpApiConnectByServerRequestProd;
import com.suray.attendance.contacts.service.AttendanceService;
import com.suray.attendance.db.service.DingUserDBService;
import com.suray.attendance.db.service.ErpOperationInfoDBService;
import com.suray.attendance.db.service.ProductionDataCollectDetailInfoDBService;
import com.suray.attendance.model.bean.AttendanceDataInfo;
import com.suray.attendance.model.bean.Mo;
import com.suray.attendance.model.enums.erp.server.RequestServerProdEnum;
import com.suray.attendance.model.pojo.ProductionDataCollectDetailInfo;
import com.suray.attendance.model.enums.business.AttendanceResultStatusEnum;
import com.suray.attendance.model.enums.erp.ErpApiServerNameEnum;
import com.suray.attendance.model.enums.yida.ReportWorkFormEnum;
import com.suray.attendance.model.pojo.ErpOperationInfo;
import com.suray.attendance.model.pojo.UserInfo;
import com.suray.attendance.service.ErpService;
import com.suray.attendance.yida.service.YiDaInstanceService;
import io.swagger.annotations.ApiOperation;
import lombok.extern.apachecommons.CommonsLog;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.util.*;
import java.util.stream.Collectors;

/**
 * Description: 考勤模块
 * Author: linyu
 * Date: 2023/11/21
 */
@RequestMapping("/api/attendance")
@RestController
@CommonsLog
public class AttendanceController {

    @Resource
    private ErpService erpService;
    @Resource
    private AttendanceService attendanceService;
    @Resource
    private DingAccessTokenService dingAccessTokenService;
    @Resource
    private DingUserDBService dingUserDBService;
    @Resource
    private YiDaInstanceService yiDaInstanceService;
    @Resource
    private ErpOperationInfoDBService erpOperationInfoDBService;
    @Resource
    private ProductionDataCollectDetailInfoDBService productionDataCollectDetailInfoDBService;


    @Async("task1")
    @Scheduled(cron = "* 15 22 * * *")
    @ApiOperation(value = "获取erp中报工信息的数据")
    @PostMapping(value = "/getProductCollectionDataFromErp")
    public Result getProductCollectionDataFromErp(){
        LocalDateTime currentDateTime = LocalDateTime.now();
        // 进行数据校验
        return getDataHandlingResult(currentDateTime);
    }

    @ApiOperation(value = "手动操作获取erp中报工信息的数据")
    @GetMapping(value = "/getProductCollectionDataFromErpByManual/{year}/{month}/{dayOfMonth}")
    public Result getProductCollectionDataFromErpByManual(@PathVariable("year")Integer year,@PathVariable("month")Integer month,@PathVariable("dayOfMonth")Integer dayOfMonth){
        LocalDateTime currentDateTime = LocalDateTime.now();
        // 进行数据校验
        if(year==null || year==0 || month==null || month==0 ||dayOfMonth==null || dayOfMonth==0){
            LoggerUtil.warn("所传输的日期参数不合法；year="+year+";month="+month+";dayOfMonth="+dayOfMonth,AttendanceController.class);
        }else {
            currentDateTime = LocalDateTime.of(year,month,dayOfMonth,12,12);
        }
        return getDataHandlingResult(currentDateTime);
    }

    private Result getDataHandlingResult(LocalDateTime currentDateTime) {
        Map<String, Object> resultMap = null;
        try {
            LocalDateTime startDateTime = currentDateTime.withDayOfMonth(1).with(LocalTime.MIN);
            LocalDateTime endDateTime = currentDateTime.toLocalDate().atTime(23, 59, 59, 999999999);
            List<ProductionDataCollectDetailInfo> productionDataCollectDetailInfoAttendanceResult = new ArrayList<>();
            List<String[]> queryMattendance = new ArrayList<String[]>();
            queryMattendance.add(new String[]{"production_date",startDateTime.toString(),">="});
            queryMattendance.add(new String[]{"production_date",endDateTime.toString(),"<="});
            queryMattendance.add(new String[]{"ApproveStatus","Y","="});
            queryMattendance.add(new String[]{"workstation_no","AG","like"});
            List<String[]> orderMattendance = new ArrayList<String[]>();
            orderMattendance.add(new String[]{"wo_no", "desc"});
            // 泛型擦除了 TODO: 记得对审核状态做范围限制
            List<Object> productCollectionDataObjectList = erpService.queryCondition(ErpApiServerNameEnum.考勤报工收集单.name(), queryMattendance, orderMattendance, RequestServerProdEnum.自定义正式.getRequestProd());
            if(productCollectionDataObjectList == null || productCollectionDataObjectList.size()<1){
                return Result.fail(startDateTime+"到"+endDateTime+"的erp工厂为AG和AG01的已审核报工数据为空");
            }
            // 将刚刚从erp获取到的工厂为AG和AG01的报工数据转换为 List<ProductionDataCollectDetailInfo> 并处理钉钉中没有对应人员的报工数据
            List<ProductionDataCollectDetailInfo> productionDataCollectDetailInfoList = getProductionDataCollectDetailInfos(productionDataCollectDetailInfoAttendanceResult, productCollectionDataObjectList);
            // 根据eri_id排除已经处理过的报工数据 并处理钉钉中没有对应人员的报工数据
            List<ProductionDataCollectDetailInfo> newProductionDataCollectDetailInfoList = filterExistProductionDataCollectDetailInfosByErpIdAndExcludeUserIdIsNull(productionDataCollectDetailInfoAttendanceResult, productionDataCollectDetailInfoList);
            if(newProductionDataCollectDetailInfoList==null || newProductionDataCollectDetailInfoList.size()<1){
                return Result.ok(startDateTime+"到"+endDateTime+"的erp工厂为AG和AG01的已审核报工需要处理的数据为空");
            }
            // 获取工单下的预计产量，我们先将上面的报工数据根据工单编号进行分类
            supplementPlanQtyToProductionDataCollectDetailByWoNo(newProductionDataCollectDetailInfoList);
            // 宜搭考勤校验
            String accessToken = dingAccessTokenService.getAccessToken();
            // 对上面的报工数据进行分类
            Map<String, List<ProductionDataCollectDetailInfo>> productionDataCollectDetailInfoListMap = newProductionDataCollectDetailInfoList.stream()
                    .filter(productionDataCollectDetailInfo -> productionDataCollectDetailInfo.getUserId()!=null)
                    .collect(Collectors.groupingBy(productionDataCollectDetailInfo -> productionDataCollectDetailInfo.getUserId()));
            handleAttendanceDate(startDateTime, endDateTime, productionDataCollectDetailInfoAttendanceResult, accessToken, productionDataCollectDetailInfoListMap);
            handleAddProdectionDataCollectDetailInfoListToYida(productionDataCollectDetailInfoAttendanceResult, accessToken);
            resultMap = new HashMap<>(3);
            resultMap.put("productionDataCollectDetailInfoAttendanceResult",productionDataCollectDetailInfoAttendanceResult);
            resultMap.put("totalNumber",productionDataCollectDetailInfoAttendanceResult.size());
            resultMap.put("dateInterval",startDateTime+" 至 "+endDateTime);
        } catch (Exception e) {
            e.printStackTrace();
            LoggerUtil.warn("处理考勤报工的数据失败，异常信息"+e.getMessage(),AttendanceController.class);
            return Result.fail("处理考勤报工的数据失败，异常信息"+e.getMessage());
        }
        return Result.ok(resultMap);
    }

    private void handleAttendanceDate(LocalDateTime startDateTime, LocalDateTime endDateTime, List<ProductionDataCollectDetailInfo> productionDataCollectDetailInfoAttendanceResult, String accessToken, Map<String, List<ProductionDataCollectDetailInfo>> productionDataCollectDetailInfoListMap) {
        Iterator<Map.Entry<String, List<ProductionDataCollectDetailInfo>>> productionDataCollectDetailInfoListMapIterator = productionDataCollectDetailInfoListMap.entrySet().iterator();
        while (productionDataCollectDetailInfoListMapIterator.hasNext()) {
            Map.Entry<String, List<ProductionDataCollectDetailInfo>> next = productionDataCollectDetailInfoListMapIterator.next();
            String userId = next.getKey();
            if(!StringUtils.isEmpty(userId)){
                List<ProductionDataCollectDetailInfo> productionDataCollectDetailInfoListByUserId = next.getValue();
                Map<Date, AttendanceDataInfo> attendanceListRecordByUserIdFromDingDing = attendanceService.getAttendanceListRecordByUserIdFromDingDing(accessToken, userId,startDateTime, endDateTime);
                if(attendanceListRecordByUserIdFromDingDing!=null && attendanceListRecordByUserIdFromDingDing.size()>0){
                    Iterator<Map.Entry<Date, AttendanceDataInfo>> iterator = attendanceListRecordByUserIdFromDingDing.entrySet().iterator();
                    while (iterator.hasNext()) {
                        Map.Entry<Date, AttendanceDataInfo> attendanceMap = iterator.next();
                        Date date = attendanceMap.getKey();
                        ZoneId zoneId = ZoneId.systemDefault();
                        LocalDate attendanceDate = date.toInstant().atZone(zoneId).toLocalDate();
                        // 准备对在处理时间区间内根据userId为分类的列表中对productionDataCollectDetailInfoList进行过滤，请筛选出这个人这一天的报工数据
                        List<ProductionDataCollectDetailInfo> collectByAttendanceDate = productionDataCollectDetailInfoListByUserId.stream().filter(productionDataCollectDetailInfo -> productionDataCollectDetailInfo.getProduction_date().substring(0,10).equals(attendanceDate.toString())).collect(Collectors.toList());
                        if(collectByAttendanceDate!=null && collectByAttendanceDate.size()>0){
                            Long workTimeSum = collectByAttendanceDate.stream().mapToLong(productionDataCollectDetailInfoEntity -> productionDataCollectDetailInfoEntity.getLabor_hours()).sum();
                            AttendanceDataInfo attendanceDataInfo = attendanceMap.getValue();
                            Long workTime = attendanceDataInfo.getWorkTime();
                            String outcome = attendanceDataInfo.getOutcome();
                            String startWorkTime = attendanceDataInfo.getStartWorkTime();
                            String endWorkTime = attendanceDataInfo.getEndWorkTime();
                            String startWorkResult = attendanceDataInfo.getStartWorkResult();
                            String endWorkResult = attendanceDataInfo.getEndWorkResult();
                            collectByAttendanceDate.stream().forEach(productionDataCollectDetailInfoEntity->{
                                productionDataCollectDetailInfoEntity.setAttendanceResult(AttendanceResultStatusEnum.正常.name()+";"+outcome);
                                if(workTimeSum>workTime){
                                    productionDataCollectDetailInfoEntity.setAttendanceResult(AttendanceResultStatusEnum.超时.name());
                                }
                                productionDataCollectDetailInfoEntity.setDingWorkTime(workTime);
                                productionDataCollectDetailInfoEntity.setUserId(userId);
                                productionDataCollectDetailInfoEntity.setStartWorkTime(startWorkTime);
                                productionDataCollectDetailInfoEntity.setEndWorkTime(endWorkTime);
                                productionDataCollectDetailInfoEntity.setStartWorkResult(startWorkResult);
                                productionDataCollectDetailInfoEntity.setEndWorkResult(endWorkResult);
                                productionDataCollectDetailInfoAttendanceResult.add(productionDataCollectDetailInfoEntity);
                            });
                        }
                    }
                }else {
                    LoggerUtil.info("userId="+userId+"该用户在时间区间为"+startDateTime+"~"+endDateTime+"的考勤数据为空", AttendanceController.class);
                }
            }
        }
    }

    private void handleAddProdectionDataCollectDetailInfoListToYida(List<ProductionDataCollectDetailInfo> productionDataCollectDetailInfoAttendanceResult, String accessToken) {
        // TODO 暂时不使用批量新增宜搭数据，但是使用的接口还是批量的、
        // 开始productionDataCollectDetailInfoAttendanceResult 遍历该集合进行数据处理
        productionDataCollectDetailInfoAttendanceResult.stream().forEach(productionDataCollectDetailInfo -> {
            try {
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                Map<String, Object> productionDataCollectHashMap = new HashMap<>();
                productionDataCollectHashMap.put(ReportWorkFormEnum.生产资料收集明细_工单.getFieldId(),productionDataCollectDetailInfo.getWo_no());
                productionDataCollectHashMap.put(ReportWorkFormEnum.工作中心.getFieldId(),productionDataCollectDetailInfo.getWorkstation_name());
                productionDataCollectHashMap.put(ReportWorkFormEnum.项目.getFieldId(),productionDataCollectDetailInfo.getProject_name());
                // 工艺id工艺名称需要做数据对应
                String technology_code = productionDataCollectDetailInfo.getTechnology_code();
                ErpOperationInfo operationInfo = erpOperationInfoDBService.getOperationNameById(technology_code);
                productionDataCollectHashMap.put(ReportWorkFormEnum.工艺名称.getFieldId(),operationInfo.getOperationName());
                productionDataCollectHashMap.put(ReportWorkFormEnum.员工姓名.getFieldId(),productionDataCollectDetailInfo.getEmployee_name());
                productionDataCollectHashMap.put(ReportWorkFormEnum.员工编号.getFieldId(),productionDataCollectDetailInfo.getEmployee_no());
                String production_date = productionDataCollectDetailInfo.getProduction_date();
                SimpleDateFormat ft = new SimpleDateFormat("yyyy-MM-dd");
                Date productionDate = null;
                try {
                    productionDate = ft.parse(production_date);
                } catch (ParseException e) {
                    e.printStackTrace();
                }
                productionDataCollectHashMap.put(ReportWorkFormEnum.生产日期.getFieldId(),productionDate);
                productionDataCollectHashMap.put(ReportWorkFormEnum.生产资料收集明细_品号.getFieldId(),productionDataCollectDetailInfo.getItem_code());
                productionDataCollectHashMap.put(ReportWorkFormEnum.生产资料收集明细_品名.getFieldId(),productionDataCollectDetailInfo.getItem_name());
                productionDataCollectHashMap.put(ReportWorkFormEnum.生产资料收集明细_规格.getFieldId(),productionDataCollectDetailInfo.getItem_specification());
                productionDataCollectHashMap.put(ReportWorkFormEnum.生产资料收集明细_数量.getFieldId(),productionDataCollectDetailInfo.getQty());
                productionDataCollectHashMap.put(ReportWorkFormEnum.生产资料收集明细_单位名称.getFieldId(),productionDataCollectDetailInfo.getUnit_name());
                productionDataCollectHashMap.put(ReportWorkFormEnum.使用人时.getFieldId(),productionDataCollectDetailInfo.getLabor_hours());
                productionDataCollectHashMap.put(ReportWorkFormEnum.钉钉考勤工时.getFieldId(),productionDataCollectDetailInfo.getDingWorkTime());
                productionDataCollectHashMap.put(ReportWorkFormEnum.考勤结果.getFieldId(),productionDataCollectDetailInfo.getAttendanceResult());
                productionDataCollectHashMap.put(ReportWorkFormEnum.上班打卡时间.getFieldId(),productionDataCollectDetailInfo.getStartWorkTime());
                productionDataCollectHashMap.put(ReportWorkFormEnum.下班打卡时间.getFieldId(),productionDataCollectDetailInfo.getEndWorkTime());
                productionDataCollectHashMap.put(ReportWorkFormEnum.预计产量.getFieldId(),productionDataCollectDetailInfo.getPlan_qty());
                List<String> formDataJsonList = new ArrayList<>(1);
                String reportWorkJson = JSON.toJSONString(productionDataCollectHashMap);
                formDataJsonList.add(reportWorkJson);
                List<String> instanceIdList = yiDaInstanceService.batchProductionDataCollectDetailInfoToYiDa(accessToken,formDataJsonList);
                if(instanceIdList != null && instanceIdList.size()>0){
                    String instanceId = instanceIdList.get(0);
                    productionDataCollectDetailInfo.setYida_id(instanceId);
                    productionDataCollectDetailInfo.setCreateTime(LocalDateTime.now());
                    // 新增该条数据至数据库的报工表中
                    Boolean addSuccess = productionDataCollectDetailInfoDBService.add(productionDataCollectDetailInfo);
                    if(!addSuccess){
                        LoggerUtil.error("该报工数据新增失败"+JSON.toJSONString(productionDataCollectDetailInfo), AttendanceController.class);
                        // TODO 再次重试新增至数据库 如果还是不可以则根据 instanceId直接去宜搭的表中删除该条记录
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
                LoggerUtil.error("处理最后的报工数据失败"+JSON.toJSONString(productionDataCollectDetailInfo)+"异常信息"+e.getMessage(),AttendanceController.class);
            }
        });
    }

    /**
    * @Description: 根据eri_id排除已经处理过的报工数据 并处理钉钉中没有对应人员的报工数据
    * @Param: [productionDataCollectDetailInfoAttendanceResult, productionDataCollectDetailInfoList]
    * @return: java.util.List<com.suray.attendance.model.pojo.ProductionDataCollectDetailInfo>
    * @Author: jly
    * @Date: 2023/12/1
    */
    private List<ProductionDataCollectDetailInfo> filterExistProductionDataCollectDetailInfosByErpIdAndExcludeUserIdIsNull(List<ProductionDataCollectDetailInfo> productionDataCollectDetailInfoAttendanceResult, List<ProductionDataCollectDetailInfo> productionDataCollectDetailInfoList) {
        return productionDataCollectDetailInfoList.stream().filter(productionDataCollectDetailInfo -> {
                String erp_id = productionDataCollectDetailInfo.getErp_id();
                ProductionDataCollectDetailInfo productionDataCollectDetailInfoByErpId = productionDataCollectDetailInfoDBService.getProductionDataCollectDetailInfoByErpId(erp_id);
                if (productionDataCollectDetailInfoByErpId != null && !StringUtils.isEmpty(productionDataCollectDetailInfoByErpId.getErp_id())) {
                    return false;
                }
                String employeeName = productionDataCollectDetailInfo.getEmployee_name();
                UserInfo userInfoByName = dingUserDBService.getUserInfoByName(employeeName);
                if(userInfoByName!=null && !StringUtils.isEmpty(userInfoByName.getId())){
                    productionDataCollectDetailInfo.setUserId(userInfoByName.getId());
                }else {
                    // 钉钉中没有对应的人
                    productionDataCollectDetailInfo.setAttendanceResult(AttendanceResultStatusEnum.无法校验.name()+";钉钉没有该人员");
                    productionDataCollectDetailInfoAttendanceResult.add(productionDataCollectDetailInfo);
                }
                return true;
            }).collect(Collectors.toList());
    }

    /**
    * @Description: 获取工单下的预计产量，我们先将上面的报工数据根据工单编号进行分类
    * @Param: [productionDataCollectDetailInfoList]
    * @return: void
    * @Author: jly
    * @Date: 2023/11/30
    */
    private void supplementPlanQtyToProductionDataCollectDetailByWoNo(List<ProductionDataCollectDetailInfo> productionDataCollectDetailInfoList) {
        // 因为要获取工单下的预计产量，我们先将上面的报工数据根据工单编号进行分类
        Map<String, List<ProductionDataCollectDetailInfo>> productionDataCollectDetailInfoWoNoMap = productionDataCollectDetailInfoList.stream()
                .collect(Collectors.groupingBy(productionDataCollectDetailInfo -> productionDataCollectDetailInfo.getWo_no()));
        Iterator<Map.Entry<String, List<ProductionDataCollectDetailInfo>>> iteratorProductionDataCollectDetailInfoWoNoMap = productionDataCollectDetailInfoWoNoMap.entrySet().iterator();
        while (iteratorProductionDataCollectDetailInfoWoNoMap.hasNext()) {
            Map.Entry<String, List<ProductionDataCollectDetailInfo>> woNoMap = iteratorProductionDataCollectDetailInfoWoNoMap.next();
            String wo_no = woNoMap.getKey();
            // 调用erp的维护工单的查询编号
            Mo mo = (Mo) erpService.read(ErpApiServerNameEnum.维护工单.name(), wo_no,RequestServerProdEnum.OAPI正式.getRequestProd());
            if(mo!=null && !StringUtils.isEmpty(mo.getDoc_no())){
                BigDecimal plan_qty = mo.getPlan_qty();
                List<ProductionDataCollectDetailInfo> productionDataCollectDetailInfoListByWoNo = woNoMap.getValue();
                productionDataCollectDetailInfoList.stream().forEach(productionDataCollectDetailInfo -> {
                    productionDataCollectDetailInfo.setPlan_qty(plan_qty);
                });
            }else {
                LoggerUtil.error(wo_no+"该工单的数据读取失败", AttendanceController.class);
            }
        }
    }

    /**
    * @Description: 将刚刚从erp获取到的工厂为AG和AG01的报工数据转换为 List<ProductionDataCollectDetailInfo>
    * @Param: [productionDataCollectDetailInfoAttendanceResult, productCollectionDataObjectList]
    * @return: java.util.List<com.suray.attendance.model.pojo.ProductionDataCollectDetailInfo>
    * @Author: jly
    * @Date: 2023/11/30
    */
    private List<ProductionDataCollectDetailInfo> getProductionDataCollectDetailInfos(List<ProductionDataCollectDetailInfo> productionDataCollectDetailInfoAttendanceResult, List<Object> productCollectionDataObjectList) {
        try {
            return productCollectionDataObjectList.stream().map(productCollectionDataObject -> {
                    ProductionDataCollectDetailInfo productionDataCollectDetailInfo = JSONObject.parseObject(JSON.toJSONString(productCollectionDataObject), ProductionDataCollectDetailInfo.class);
                    return productionDataCollectDetailInfo;
                }).collect(Collectors.toList());
        } catch (Exception e) {
            e.printStackTrace();
            LoggerUtil.error("数据转换失败：将刚刚从erp获取到的工厂为AG和AG01的报工数据转换为 List<ProductionDataCollectDetailInfo> 并处理钉钉中没有对应人员的报工数据，异常信息"+e.getMessage(),AttendanceController.class);
        }
        return null;
    }


}
